﻿using System;
using System.Web.Mvc;
using DotNetOpenAuth.AspNet;
using Microsoft.Web.WebPages.OAuth;
using User.EF.Contracts;
using User.EF.Model;
using User.Global.Constvalues.Web.Navigation;
using User.Global.Contracts.Mail;
using User.Global.Contracts.Membership;
using User.Global.Contracts.Message;
using User.Global.Resources;
using User.Web.Common.Membership;
using User.Web.Models.Account;
using User.Web.UI.Controllers.BaseControllers;
using User.Web.UI.Helper.RightsManagement;

namespace User.Web.UI.Controllers
{
    [CustomAuthorize]
    public class AccountController : ControllerBaseUser
    {
        #region Member
        /// <summary>
        /// Unser Repository für die Mitarbeiterabfragen
        /// </summary>
        private readonly IDiMembership<Member> _membership;

        /// <summary>
        /// User Repository für alle allgemeinen Abfragen fürs Web.
        /// </summary>
        private readonly IWebQuerySummary _webQueries;

        /// <summary>
        /// Mailverwaltung für die Accountverwaltung
        /// </summary>
        private readonly IAccountMails _accountMails;
        #endregion

        #region Initialize
        public AccountController(IDiMembership<Member> membership, IAccountMails accountMails, IWebQuerySummary webQueries)
        {
            if (membership == null)
            {
                throw new NullReferenceException("IDiMembership is Null");
            }

            if (accountMails == null)
            {
                throw new NullReferenceException("AccountMails is Null");
            }

            if (webQueries == null)
            {
                throw new NullReferenceException("WebQuery is Null");
            }

            _webQueries = webQueries;
            _accountMails = accountMails;
            _membership = membership;
        }
        #endregion

        #region Actions
        /// <summary>
        /// Anzeige der Passwort Recovery Seite auf der man seinen Usernamen (Email) eingetragen werden kann.
        /// </summary>
        [AllowAnonymous]
        public ActionResult RecoverPassword()
        {
            RecoveryPasswordModel model = new RecoveryPasswordModel();
            return View("RecoverPassword", model);
        }

        /// <summary>
        /// Anzeige der Passwort Recovery Seite auf der man seinen Usernamen (Email) eingetragen werden kann.
        /// </summary>
        [HttpPost]
        [ValidateAntiForgeryToken]
        [AllowAnonymous]
        public ActionResult RecoverPassword(RecoveryPasswordModel model)
        {
            model.SetCommunication(Logger, AddMessage);
            //Versenden der PW Recovery Email
            if (ModelState.IsValid)
            {
                model.SendRecoveryMail(_membership, _accountMails);
                return RedirectToAction("Index", "Home");
            }

            return View("RecoverPassword", model);
        }

        /// <summary>
        /// Model Anzeigen für das erneute Setzen eines Passworts.
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        public ActionResult RecoverySetNewPassword(string token)
        {
            //Wenn kein Token übergeben wurde, direkt auf die Startseite verweisen.
            if (string.IsNullOrEmpty(token))
            {
                return RedirectToAction("Index", "Home");
            }

            RecoverySetNewPasswordModel model = new RecoverySetNewPasswordModel();
            model.Token = token; //Token merken

            return View("RecoverSetNewPassword", model);
        }

        /// <summary>
        /// Postback Model für das erneute Setzten eines Passworts.
        /// </summary>
        [HttpPost]
        [ValidateAntiForgeryToken]
        [AllowAnonymous]
        public ActionResult RecoverySetNewPassword(RecoverySetNewPasswordModel model)
        {
            model.SetCommunication(Logger, AddMessage);

            if (ModelState.IsValid)
            {
                model.ChangePassword(_membership);
                return RedirectToAction("Index", "Home");
            }

            return View("RecoverSetNewPassword", model);
        }

        /// <summary>
        /// Seite zum Registrieren eines neuen Benutzers
        /// </summary>
        [AllowAnonymous]
        public ActionResult Register()
        {
            RegisterModel model = new RegisterModel();
            return View("Register", model);
        }

        /// <summary>
        /// PostBack wenn ein User seine Daten zum Registrieren eingegeben hat.
        /// </summary>
        /// <param name="model">Das Ausgefüllte Model mit den passenden Userdaten</param>
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            model.SetCommunication(Logger, AddMessage);

            if (ModelState.IsValid && model.CreateUser(_membership))
            {
                return RedirectToAction("RegisterSuccess");
            }

            return View("Register", model);
        }

        /// <summary>
        /// Wenn die Registrierung erfolgreich war, wird diese seite angezeigt.
        /// </summary>
        [AllowAnonymous]
        public ActionResult RegisterSuccess()
        {
            return View("RegisterSuccess");
        }

        /// <summary>
        /// Login View anzeigen
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        public ActionResult Login()
        {
            return View("Login");
        }

        /// <summary>
        /// Login mit den aktuell eingegebenen Userdaten.
        /// </summary>
        /// <param name="model">die aktuellen LoginModeldaten</param>
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(LoginModel model)
        {
            model.SetCommunication(Logger, AddMessage);

            //Modelstate prüfen und wenn dieser passt, dann den Login ausführen
            if (ModelState.IsValid && _membership.Login(model.Username, model.Password, true))
            {
                return RedirectToAction("Index", "Home");
            }

            return View("Login", model);
        }

        /// <summary>
        /// Ausloggen des aktuellen Users
        /// </summary>
        [AllowAnonymous]
        public ActionResult Logoff()
        {
            _membership.Logout();
            return RedirectToAction("Index", "Home");
        }

        /// <summary>
        /// Bearbeiten des aktuell eingeloggten Users
        /// </summary>
        /// <param name="id">Die passende UserId die bearbeitet werden soll.</param>
        public ActionResult MemberEdit(long id)
        {
            //Das passende Membermodel für den aktuell eingeloggten User laden.
            MemberEditModel model = new MemberEditModel();
            model.SetCommunication(Logger, AddMessage);
            if (model.Load(_webQueries, id))
            {
                return View("MemberEdit", model);
            }

            return RedirectToAction("Index", "Home");
        }

        /// <summary>
        /// Speichern des aktuell eingeloggten Users
        /// </summary>
        /// <param name="id">Die passende UserId die bearbeitet/gespeichert werden soll.</param>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult MemberEdit(MemberEditModel model, long id)
        {
            if (ModelState.IsValid)
            {
                model.SetCommunication(Logger, AddMessage);
                //Das passende Membermodel für den aktuell eingeloggten User laden.
                if (model.Save(_webQueries, id))
                {
                    long userId = id;
                    //Redirect To Action damit F5 verhindert wird!
                    return RedirectToAction("MemberEdit", "Account", new { id = userId });
                }

                return RedirectToAction("Index", "Home");
            }

            return View("MemberEdit", model);
        }

        /// <summary>
        /// Ändern des Passworts, wenn der Benutzer eingeloggt ist.
        /// </summary>
        /// <param name="id">Die UserId für die das Passwort geändert werden soll</param>
        public ActionResult ChangePassword(long id)
        {
            ChangePasswordModel model = new ChangePasswordModel();
            return View("ChangePassword", model);
        }

        /// <summary>
        /// Ändern des Passworts, wenn der Benutzer eingeloggt ist.
        /// </summary>
        /// <param name="model">Das aktuelle PasswortModel</param>
        /// <param name="id">Die UserId für die das Passwort geändert werden soll</param>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult ChangePassword(ChangePasswordModel model, long id)
        {
            if (ModelState.IsValid)
            {
                model.SetCommunication(Logger, AddMessage);
                if (model.ChangePassword(_membership, id))
                {
                    return RedirectToAction("Index", "Home");
                }
            }

            return View("ChangePassword", model);
        }


        /// <summary>
        /// Anzeigen der Benutzerdetails, von der übergebenen Id
        /// </summary>
        /// <param name="id">User Id die angezeigt werden soll</param>
        public ActionResult ProviderConnect(int id)
        {
            return View();
        }


        #endregion

        #region OAuth Functions
        /// <summary>
        /// Für die Authentifizierung von OAuth zuständig, wird aufgerufen wenn der jeweilige Providerlogin geklickt wurde.
        /// </summary>
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            return new ExternalLoginResult(provider, Url.Action(EActionAccount.ExternalLoginCallback.ToString(), new { ReturnUrl = returnUrl }));
        }

        /// <summary>
        /// Für die Authentifizierung von OAuth zuständig.
        /// </summary>
        [AllowAnonymous]
        public ActionResult ExternalLoginCallback(string returnUrl)
        {
            //Beinhaltet alle wichtigen "Callbackdaten" vom jeweiligen Provider über den Ihr euch eingeloggt habt.
            AuthenticationResult authResult = OAuthWebSecurity.VerifyAuthentication(Url.Action(EActionAccount.ExternalLoginCallback.ToString(), new { ReturnUrl = returnUrl }));
            IOAuthMembership<Member> logonOAuth = new OAuthMembership(AddMessage, authResult, _webQueries);
            //Prüfen ob der Login erfolgreich war über die externe Authentifizierung
            if (logonOAuth.Login())
            {
                //Prüfen ob auf eine bestimmt URL weitergeleitet werden soll.
                if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                {
                    return Redirect(returnUrl);
                }
                else
                {
                    //Auf die Startseite verweisen
                    return RedirectToAction("Index", "Home");
                }
            }

            //Direkt auf den Login wieder umleiten, wenn der Login nicht erfolgreich war.
            return RedirectToAction("Login", "Account");
        }


        /// <summary>
        /// Für die Authentifizierung von OAuth zuständig, wird aufgerufen wenn der jeweilige Providerlogin geklickt wurde. Wenn ein User eingeloggt ist und 
        /// seinen Account zusätzlich mit einem Provider verbinden möchte.
        /// </summary>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult ProviderConnect(string provider, string returnUrl)
        {
            return new ExternalLoginResult(provider, Url.Action(EActionAccount.ProviderConnectCallback.ToString(), new { ReturnUrl = returnUrl }));
        }

        /// <summary>
        /// Für die Authentifizierung von OAuth zuständig, wenn ProviderConnect fertig ist.
        /// </summary>
        [AllowAnonymous]
        public ActionResult ProviderConnectCallback(string returnUrl)
        {
            //Beinhaltet alle wichtigen "Callbackdaten" vom jeweiligen Provider über den Ihr euch eingeloggt habt.
            AuthenticationResult authResult = OAuthWebSecurity.VerifyAuthentication(Url.Action(EActionAccount.ProviderConnectCallback.ToString(), new { ReturnUrl = returnUrl }));
            IOAuthMembership<Member> logonOAuth = new OAuthMembership(AddMessage, authResult, _webQueries);
            //Den aktuellen Account mit dem Provider verbinden.
            if (!logonOAuth.JoinProviderAccount())
            {
                AddMessage.Add(ErrorMessages.ProviderJoinError, MessageTypes.error);
            }

            return RedirectToAction(EActionAccount.MemberEdit.ToString(), EController.Account.ToString(), new { Id = User.Identity.Name });
        }
        #endregion
    }
}
